""" Widgets that serve as controls for the user. """

from operator import attrgetter

from pygame import (
    Rect,
    Surface,
    MOUSEBUTTONDOWN,
    MOUSEBUTTONUP,
    MOUSEMOTION,
    VIDEORESIZE
)

import sbak.draw
from sbak.draw import (
    rect_ as draw_rect_,
    text as draw_text,
    fix_color
)
from sbak.event import (
    Event,
    CONTROLEVENT,
    WIDGETEVENT,
    WE_CLICKED,
    Responder
)
from sbak.widget import (
    AbstractWidget,
    Widget
)


__all__ = [
    'Button',
]

class Button(Widget):
    """ A clickable button.
    
        btn = Button(region = None, text = "", handler = None, id = None)
        
        ~~~ Constructor Arguments ~~~
        
        region:     Region of the parent window that the button
                    occupies.
        
        text:       Text to put on the button.
        
        action:     A function that will act as the handler for the
                    WE_CLICKED widget event. This will be hooked with
                    the button so that it will be triggered whenever
                    the button receives a WE_CLICKED widget event
                    that has the button as its widget.
        
        post_clicked:
                    See the attribute of the same name. (Below.)
        
        id:         A unique ID string for the button.
        
        ~~~ Description ~~~
    
        Implements handlers for MOUSEBUTTONDOWN and MOUSEBUTTONUP, and
        responds to a complete down-up click by posting a WE_CLICKED
        widget event to the root EQM.
        
        Overrides the Widget.draw_box functionality by adding a text
        string centered at the middle of the box, and by changing the
        box color when in a pressed-in state.
        
        ~~~ Attributes ~~~
        
        post_clicked:
        
            If True, the button will post a WE_CLICKED widget event to
            the root node of the event tree that it is in whenever the
            mouse is pressed and released on the button. If False
            (default), the button will not post the event to the root
            node, but will instead self-notify immediately.
            
            Be careful when using this attribute. Because every button
            will probably receive WE_CLICKED when the button is
            clicked, you must make sure that each button only responds
            to WE_CLICKED events that correspond to each respective
            button--otherwise, pressing one button will be effectively
            the same as pressing all buttons at once!
        
    """
    
    def __init__(self,  region = None,
                        text = "",
                        action = None,
                        post_clicked = False,
                        id = None):
        Widget.__init__(self, region, id=id)
        self._text = text
        self._pressed = False
        self.post_clicked = post_clicked
        
        hooks = {
            MOUSEBUTTONDOWN : self.handle_mousebuttondown,
            MOUSEBUTTONUP : self.handle_mousebuttonup
        }
        for type, hnd in hooks.iteritems():
            self.hook(hnd, type)
            
        if action is not None:
            self.hook(
                action,
                WIDGETEVENT,
                # This line is not a typo! It is SUPPOSED to be "action".
                action = WE_CLICKED,
                widget = self
            )
            
    
    def set_text(self, text):
        self._text = text
        self.smudge()
    text = property(
        fget = attrgetter('_text'),
        fset = set_text,
        doc =
        """ Button's text.
            
            If changed, the button will be smudged.
        """
    )
    
    def draw_internal(self, dest, region):
        """ Draws the widget as a button with text. """
        region = Rect(region)
        
        # Determine the background color
        bgcolor = {True : "#FFFFFF",
                   False :  "#CCCCCC"}[bool(self._pressed)]
        
        # Draw the box
        sbak.draw.rect(dest, bgcolor, region)
        sbak.draw.rect(dest, "#000000", region, 1)
        
        # Draw the text.
        sbak.draw.text(dest,
            pos = region.center,
            text = self.text,
            fgcolor = "#000000",
            align = "center",
            valign = "middle")
    
    def handle_mousebuttondown(self, e):
        if self.region_abs.collidepoint(e.pos):
            self._pressed = True
            return True
    
    def handle_mousebuttonup(self, e):
        """ MOUSEBUTTONUP handler for Button.
        
            If the button is in a pressed state and the mouse pointer
            is over the button, then one of two things will happen
            depending on the value of the button's `post_clicked`
            attribute:
            
            * If `post_clicked` is true, the button will post a
            WE_CLICKED widget event to the event queue of the root
            node of the button's tree.
            
            * If `post_clicked` is false, the button will immediately
            self-notify with a WE_CLICKED widget event, and the value
            returned by `self.notify` will be returned by the handler.
            (This happens after the button changes it's its pressed
            status to "unpressed" and smudges itself.)
            
            In either case, the WE_CLICKED widget event that is
            generated will have the button as the event widget, the
            button's tag as the event tag, and the button's id as the
            event id.
        """
        
        if self._pressed:
            if self.region_abs.collidepoint(e.pos):
                
                newe = Event(
                    WIDGETEVENT,
                    action = WE_CLICKED,
                    id = self.id,
                    tag = self.tag,
                    widget = self
                )
                
                if self.post_clicked:
                    self.get_root().post(newe)
                
                self._pressed = False
                
                if not self.post_clicked:
                    return self.notify(newe)
                
                return True
            
            else:
                self._pressed = False